<?php
/**
 * <b>RUBIK IT Solution & Development ltd.</b>
 *
 * PROJECT		: Rubik Ultimate Framework
 * @version 	: 1.1
 * COPYRIGHT    : 2008
 * ------------------------------------------------------
 * 
 * Created on	:  Jun 18, 2008
 * 
 */

// no direct access
defined ( '_FW_EXEC' ) or die ( 'Restricted access' );

RLibImporter::cls( "database.JDatabase" );
RCoreImporter::cls( "misc.SelectResult" );

class Database extends JDatabase {
	/**
	 * Returns a reference to the global Database object, only creating it
	 * if it doesn't already exist.
	 *
	 * The 'driver' entry in the parameters array specifies the database driver
	 * to be used (defaults to 'mysql' if omitted). All other parameters are
	 * database driver dependent.
	 *
	 * @param array Parameters to be passed to the database driver
	 * @return Database A database object
	 * @since 1.5
	 */
	function &getInstance($options = array()) {
		static $instances;
		
		if (! isset ( $instances )) {
			$instances = array ();
		}
		
		$signature = serialize ( $options );
		if (empty ( $instances [$signature] )) {
			$driver = array_key_exists ( 'driver', $options ) ? $options ['driver'] : 'mysql';
			$select = array_key_exists ( 'select', $options ) ? $options ['select'] : true;
			$database = array_key_exists ( 'database', $options ) ? $options ['database'] : null;
			
			$driver = preg_replace ( '/[^A-Z0-9_\.-]/i', '', $driver );
			$path = dirname ( __FILE__ ) . DS . 'database' . DS . $driver . '.php';
			
			if (file_exists ( $path )) {
				require_once ($path);
			} else {
				//				RError::setErrorHandling(E_ERROR, 'die'); //force error type to die
				$error = RError::raiseError ( 500, SysLanguage::_ ( 'Unable to load Database Driver:' ) . $driver );
				return $error;
			}
			
			$adapter = 'Database' . $driver;
			$instance = new $adapter ( $options );
			if ($error = $instance->getErrorMsg ()) {
				//				RError::setErrorHandling(E_ERROR, 'die'); //force error type to die
				$error = RError::raiseError ( 500, SysLanguage::_ ( 'Unable to connect to the database:' ) . $error );
				return $error;
			}
			
			$instances [$signature] = & $instance;
		}
		
		return $instances [$signature];
	}
	
	/**
	 * Get the select result of a SELECT sql
	 *
	 * @param AbstractRubikDBObject $sqlOrFinder
	 * @param int $recordPerPage
	 * @param int $indexPage
	 * 
	 * @return SelectResult
	 */
	function &getSelectResult($sqlOrFinder, $recordPerPage = null, $indexPage = 1, $prefix = "#__") {
		$result = new SelectResult ( );
		$sQuery = null;
		if (! empty ( $recordPerPage )) {
			// for DBO object
			if (is_object ( $sqlOrFinder )) {
				// in this case --> this is a finder or a DTO.
				$sQuery = $sqlOrFinder->getSelectCountSQL ();
			} else {
//				$sQuery = RBaseHelper::getSelectCountFromSelectSQL ( $sqlOrFinder );
				$sQuery = "SELECT count(*) FROM ({$sqlOrFinder}) _count"; 
			}
			$this->setQuery ( $sQuery, 0, 0, $prefix );
			
			$iRsD = $this->loadResult ();
			if ($iRsD === false) {
				RError::raiseWarning ( $this->ErrorNo (), $this->getErrorMsg () );
				return false;
			}
			$count = $iRsD; // we knew the first field is the count. And this just have 1 record
			

			$result->setTotalRecords ( $count );
			$result->setIndexPage ( $indexPage );
			$result->setRecordPerPage ( $recordPerPage );
		}
		
		// for DBO object
		if (is_object ( $sqlOrFinder )) {
			// in this case --> this is a finder or a DTO.
			$sQuery = $sqlOrFinder->getSelectSQL ();
		} else {
			$sQuery = $sqlOrFinder;
		}
		if (! empty ( $recordPerPage )) {
			if ($indexPage == 0) {
				$start = 0;
			} else {
				$start = ($indexPage - 1) * $recordPerPage;
			}
		}
		$this->setQuery ( $sQuery, $start, $recordPerPage, $prefix );
		$result->recordSet = $this->loadObjectList ();
		return $result;
	}
	
	/**
	 * Save a DTO into database
	 *
	 * @param AbstractDTO $dto
	 * @return AbstractDTO or false
	 */
	function saveDTO(&$dto, $ignoreNull = true) {
		
		if (! $dto->checkValidData ()) {
			return false;
		}
		$isEdit = $dto->checkKeyNotDefault ();
		$sql = $dto->createChangeSQL ();
		$this->execute ( $sql );
		
		if (! $isEdit) {
			$id = $this->insertid ();
			$dto->setKeyValue ( $id );
			
		}
		return $dto;
	}
	
	/**
	 * Execute the query
	 *
	 * @param string $sql
	 * @access public
	 * @return mixed A database resource if successful, FALSE if not.
	 */
	function execute($sql) {
		$this->setQuery ( $sql );
		return $this->query ();
	}
	
	/**
	 * Get data from database and fill it into input dto
	 * Input DTO must have key value.
	 *
	 * @param AbstractDTO $dto
	 * @return AbstractDTO or false
	 */
	function &fillData(&$dto) {
		
		$sql = $dto->createSelectSQLWithKey ();
		if (! $sql) {
			return false;
		}
		$this->setQuery ( $sql );
		$obj = $this->loadObject ();
		if ($obj == null) {
			return false;
		}
		$dto->bind ( $obj );
		//		$dto->prepareViewData();
		return $dto;
	}
	
	/**
	 * Format the time stamp input into database format.
	 * Using this for convertDate to insert/update in databse
	 *
	 * @param int $ts time stamp
	 * @param string $dateType date time type. ("D", "DT", "T", "TS")
	 * 
	 * @return string/false the string that keep ts information.
	 */
	function convertTimestampToDB($ts, $dateType) {
		if (! is_numeric ( $ts )) {
			return false;
		}
		
		$formatStr = $this->getFormatDateTime ( $dateType );
		if (empty ( $formatStr )) {
			return $ts;
		}
		
		return date ( $formatStr, $ts );
	}
	
	/**
	 * Get the timestamp of database time
	 * 
	 * @param string $dbTime ex: '2008-08-30'
	 * @param string $dateType date time type. ("D", "DT", "T", "TS")
	 * 
	 * @return int
	 */
	function convertDbToTimeStamp($dbTime, $dateType) {
		$formatStr = $this->getFormatDateTime ( $dateType );
		if (empty ( $formatStr )) {
			return null;
		}
		
		RCoreImporter::cls('helper.RDateTimeHelper');
		return RDateTimeHelper::getTimeStamp($dbTime, $formatStr);
	}
	
	/**
	 * Get the format String of $dateType
	 * WARNING: this function should be override by sub class (specific for any DatabaseType).  
	 *
	 * @param string $dateType date time type. ("D", "DT", "T", "TS")
	 * @return string or null
	 */
	function getFormatDateTime($dateType) {
		$formatStr = null;
		switch ($dateType) {
			case SYM_TYPE_DATE :
				$formatStr = "Y-m-d";
				break;
			case SYM_TYPE_TIMESTAMP :
			case SYM_TYPE_DATETIME :
				$formatStr = "Y-m-d H:i:s";
				break;
			case SYM_TYPE_TIME :
				$formatStr = "H:i:s";
				break;
			default :
				break;
		}
		return $formatStr;
	}
	
	/**
	 * Insert a DTO into database
	 *
	 * @param AbstractDTO $dto
	 * @return AbstractDTO
	 */
	function insertDTO(&$dto) {
		if (! $dto->checkValidData ()) {
			return false;
		}
		$sql = $dto->createInsertSQL ();
		$this->execute ( $sql );
		$id = $this->insertid ();
		if (! empty ( $id )) {
			$dto->setKeyValue ( $id );
		}
		return $dto;
	}
	
	/**
	 * Update a DTO into database
	 *
	 * @param AbstractDTO $dto
	 * @return AbstractDTO
	 */
	function updateDTO(&$dto, $where = '') {
		if (! $dto->checkValidData ()) {
			return false;
		}
		$sql = $dto->createUpdateSQL ( true, $where );
		$this->execute ( $sql );
		return $dto;
	}

}
?>
